// Ohos is pleased to support the open source community by making Mars available.
// Copyright (C) 2016 THL A29 Limited, a Ohos company. All rights reserved.

// Licensed under the MIT License (the "License"); you may not use this file except in 
// compliance with the License. You may obtain a copy of the License at
// http://opensource.org/licenses/MIT

// Unless required by applicable law or agreed to in writing, software distributed under the License is
// distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing permissions and
// limitations under the License.


/**
 * created on : 2012-7-19
 * author : yanguoyue
 */

#include "../platform_comm.h"

#include "napi/native_api.h"

#include "../xlogger/xlogger.h"
#include "util/comm_function.h"
#include "util/var_cache.h"

#include "mars/boost/bind.hpp"
#include "mars/boost/ref.hpp"

#include "mars/comm/thread/lock.h"
#include "mars/comm/coroutine/coroutine.h"
#include "mars/comm/coroutine/coro_async.h"

namespace mars {
namespace comm {

static std::function<bool(std::string&)> g_new_wifi_id_cb;
static mars::comm::Mutex wifi_id_mutex;

void SetWiFiIdCallBack(std::function<bool(std::string&)> _cb) {
    mars::comm::ScopedLock lock(wifi_id_mutex);
    g_new_wifi_id_cb = _cb;
}
void ResetWiFiIdCallBack() {
    mars::comm::ScopedLock lock(wifi_id_mutex);
    g_new_wifi_id_cb = NULL;
}


int g_NetInfo = 0;    // global cache netinfo for android
WifiInfo g_wifi_info;
SIMInfo g_sim_info;
APNInfo g_apn_info;
Mutex g_net_mutex;

//DEFINE_FIND_CLASS(KPlatformCommC2Java, "com/ohos/mars/comm/PlatformComm$C2Java")
bool startAlarm(int type, int64_t id, int after) {
    xverbose_function();
/*
    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&startAlarm, type, id, after));
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    napi_status status;
    napi_value typeValue;
    status = napi_create_int32(env, type, &typeValue);
    if (status != napi_ok) {
        return false;
    }
    napi_value idValue;
    status = napi_create_int64(env, id, &idValue);
    if (status != napi_ok) {
        return false;
    }
    napi_value afterValue;
    status = napi_create_int32(env, after, &afterValue);
    if (status != napi_ok) {
        return false;
    }
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "startAlarm", 3, typeValue, idValue, afterValue);
    bool ret;
    status = napi_get_value_bool(env, result, &ret);
    if (status != napi_ok) {
        return false;
    }
    //xdebug2(TSF"id= %0, after= %1, type= %2, ret= %3", id, after, type, (bool)ret);
    return ret;
*/
    return true;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_stopAlarm, KPlatformCommC2Java, "stopAlarm", "(I)Z")
bool stopAlarm(int64_t id) {
    xverbose_function();
/*
    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&stopAlarm, id));
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    napi_value idValue;
    napi_status status = napi_create_int64(env, id, &idValue);
    if (status != napi_ok) {
        return false;
    }
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "stopAlarm", 1, idValue);
    bool ret;
    status = napi_get_value_bool(env, result, &ret);
    if (status != napi_ok) {
        return false;
    }
    xdebug2(TSF"id= %0, ret= %1", id, (bool)ret);
    return ret;
*/
    return true;
}
//static String getProxyInfo(); //retvalue: <host>:<port> //ETS to be modified
//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getProxyInfo, KPlatformCommC2Java, "getProxyInfo", "(Ljava/lang/StringBuffer;)I")
bool getProxyInfo(int& port, std::string& strProxy, const std::string& _host) {
    xverbose_function();

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&getProxyInfo, boost::ref(port), boost::ref(strProxy), _host));
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();

    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "getProxyInfo", 0);
    std::string ret;
    int status = GetNAPIStringValue(env, result, ret);
    if (status != napi_ok) {
        return false;
    }
    //host:port
    std::string::size_type pos = ret.find(':');
    if (pos == std::string::npos) {
        return false;
    }
    strProxy = ret.substr(0, pos);
    port = atoi(ret.substr(pos + 1).c_str());
    if(port <= 0) {
        return false;
    } else {
        return true;
    }
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getNetInfo, KPlatformCommC2Java, "getNetInfo", "()I")
int getNetInfo() {
	xverbose_function();
#if 0
    // if (g_NetInfo != 0 && g_NetInfo != kNoNet)
    //     return g_NetInfo;
    
    // if (coroutine::isCoroutine())
    //     return coroutine::MessageInvoke(&getNetInfo);

    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    xinfo2("getNetInfo calling JS_CallStaticMethodByName");
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "getNetInfo", 0);
    int ret;
    xinfo2("getNetInfo after call JS_CallStaticMethodByName result:%1", result);
    napi_status status = napi_get_value_int32(env, result, &ret);
    xinfo2("getNetInfo after call napi_get_value_int32 status:%1, ret:%2", status, ret);
    if (status != napi_ok) {
        return -1;
    }
    g_NetInfo = ret;
    xverbose2(TSF"netInfo= %0", ret);
#endif
    return 1;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getStatisticsNetType, KPlatformCommC2Java, "getStatisticsNetType", "()I")
int getNetTypeForStatistics(){
    xverbose_function();
#if 0
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();

    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "getStatisticsNetType", 0);
    int ret;
    napi_status status = napi_get_value_int32(env, result, &ret);
    if (status != napi_ok) {
        return -1;
    }
    return ret;
#endif
    return 1;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getCurRadioAccessNetworkInfo, KPlatformCommC2Java, "getCurRadioAccessNetworkInfo", "()I")
bool getCurRadioAccessNetworkInfo(RadioAccessNetworkInfo& _raninfo) {
    xverbose_function();
    int netType = getNetTypeForStatistics(); // change interface calling to "getNetTypeForStatistics", because of Android's network info method calling restrictions


    /**
        NETTYPE_NOT_WIFI = 0;
        NETTYPE_WIFI = 1;
        NETTYPE_WAP = 2;
        NETTYPE_2G = 3;
        NETTYPE_3G = 4;
        NETTYPE_4G = 5;
        NETTYPE_UNKNOWN = 6;	
        NETTYPE_5G = 7;
        NETTYPE_NON = -1;
    **/

    switch (netType) {
    case -1:
    case 0:
    case 6:
        break;
    case 1:
        _raninfo.radio_access_network = WIFI;
        break;

    case 2:
    case 3:
        _raninfo.radio_access_network = GPRS;
        break;

    case 4:
        _raninfo.radio_access_network = WCDMA;
        break;

    case 5:
        _raninfo.radio_access_network = LTE;
        break;

    case 7: 
        // _raninfo.radio_access_network = G5;  // consider it to "4G" though it may be real "5G".
        _raninfo.radio_access_network = LTE;
        break;

    default:
        break;
    }
    xverbose2(TSF"netInfo= %0, %1", netType, _raninfo.radio_access_network);

    return !_raninfo.radio_access_network.empty();
}



//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getCurWifiInfo, KPlatformCommC2Java,
//                          "getCurWifiInfo", "()Lcom/ohos/mars/comm/PlatformComm$WifiInfo;")
//ETS to be modfified: return array
bool getCurWifiInfo(WifiInfo& wifiInfo, bool _force_refresh) {
    xverbose_function();
#if 0
    if (!_force_refresh && !g_wifi_info.ssid.empty()) {
    	wifiInfo = g_wifi_info;
    	return true;
    }

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&getCurWifiInfo, boost::ref(wifiInfo), _force_refresh));

    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();

    ScopedLock lock(g_net_mutex);

    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "getCurWifiInfo", 0);

    bool isArray;
    int status  = napi_is_array(env, result, &isArray);
    if (status != napi_ok) {
        return false;
    }

    uint32_t arrayLength;
    status =  napi_get_array_length(env, result, &arrayLength);
    if (status != napi_ok || arrayLength != 2) {
        return false;
    }

    napi_value ssidValue, bssidValue;
    status = napi_get_element(env, result, 0, &ssidValue);
    if (status != napi_ok) {
        return false;
    }
    status = GetNAPIStringValue(env, ssidValue, g_wifi_info.ssid);
    if (status != napi_ok) {
        return false;
    }
    status = napi_get_element(env, result, 1, &bssidValue);
    if (status != napi_ok) {
        return false;        
    }
    status = GetNAPIStringValue(env, bssidValue, g_wifi_info.bssid);
    if (status != napi_ok) {
        return false;
    }

    wifiInfo = g_wifi_info;
#endif
    return true;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getCurSIMInfo, KPlatformCommC2Java, "getCurSIMInfo",
//                          "()Lcom/ohos/mars/comm/PlatformComm$SIMInfo;")
bool getCurSIMInfo(SIMInfo& simInfo) {
    xverbose_function();
#if 0
    if (!g_sim_info.isp_code.empty()) {
    	simInfo = g_sim_info;
    	return true;
    }
    
    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&getCurSIMInfo, boost::ref(simInfo)));

    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();

    ScopedLock lock(g_net_mutex);
    
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "getCurSIMInfo", 0);

    bool isArray;
    int status  = napi_is_array(env, result, &isArray);
    if (status != napi_ok) {
        return false;
    }

    uint32_t arrayLength;
    status =  napi_get_array_length(env, result, &arrayLength);
    if (status != napi_ok || arrayLength != 2) {
        return false;
    }

    napi_value ispCodeValue, ispNameValue;
    status = napi_get_element(env, result, 0, &ispCodeValue);
    if (status != napi_ok) {
        return false;
    }
    status = GetNAPIStringValue(env, ispCodeValue, g_sim_info.isp_code);
    if (status != napi_ok) {
        return false;
    }
    status = napi_get_element(env,result, 1, &ispNameValue);
    if (status != napi_ok) {
        return false;        
    }
    status = GetNAPIStringValue(env, ispNameValue, g_sim_info.isp_name);
    if (status != napi_ok) {
        return false;
    }

    simInfo = g_sim_info;
#endif
    return true;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getAPNInfo, KPlatformCommC2Java, "getAPNInfo", "()Lcom/tencent/mars/comm/PlatformComm$APNInfo;")
bool getAPNInfo(APNInfo& info) {
    xverbose_function();
    xinfo2("enter getAPNInfo calling JS_CallStaticMethodByName");
#if 0
    if (g_apn_info.nettype >= kNoNet) {
    	info = g_apn_info;
    	return true;
    }

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&getAPNInfo, boost::ref(info)));
                                        
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();

    ScopedLock lock(g_net_mutex);
    
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "getAPNInfo", 0);

    bool isArray;
    int status  = napi_is_array(env, result, &isArray);
    if (status != napi_ok) {
        return false;
    }

    uint32_t arrayLength;
    status =  napi_get_array_length(env, result, &arrayLength);
    if (status != napi_ok || arrayLength != 3) {
        return false;
    }

    napi_value netTypeValue, subNetTypeValue, extraInfoValue;
    status = napi_get_element(env, result, 0, &netTypeValue);
    if (status != napi_ok) {
        return false;
    }
    status = napi_get_value_int32(env, netTypeValue, &g_apn_info.nettype);
    if (status != napi_ok) {
        return -1;
    }   
    status = napi_get_element(env, result, 1, &subNetTypeValue);
   if (status != napi_ok) {
        return false;
    }
    status = napi_get_value_int32(env, subNetTypeValue, &g_apn_info.sub_nettype);
    if (status != napi_ok) {
        return -1;
    }
    status = napi_get_element(env, result, 2, &extraInfoValue);
   if (status != napi_ok) {
        return false;
    }
        
    status = GetNAPIStringValue(env, extraInfoValue, g_apn_info.extra_info);
    if (status != napi_ok) {
        return false;
    }

    info = g_apn_info;
#endif
    return true;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_getSignal, KPlatformCommC2Java, "getSignal", "(Z)J")
//ETS to be modified: public static long getSignal(int isWifi);
unsigned int getSignal(bool isWifi) {
    xverbose_function();
#if 0
    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&getSignal, isWifi));
                                        
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();

    napi_value isWifiValue;
    napi_status status = napi_create_int32(env, (isWifi ? 1 : 0), &isWifiValue);
    if (status != napi_ok) {
        return false;
    }
    
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "getSignal", 1, isWifiValue);

    int32_t signal;
    status  = napi_get_value_int32(env, result, &signal);
    if (status != napi_ok) {
        return false;
    }
    return (unsigned int)signal;
#endif
    return 80;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_isNetworkConnected, KPlatformCommC2Java, "isNetworkConnected", "()Z")
bool isNetworkConnected() {
    xverbose_function();
#if 0
    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(&isNetworkConnected);
                                        
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.PlatformComm.C2JS", "isNetworkConnected", 0);

    bool isConnected;
    napi_status status  = napi_get_value_bool(env, result, &isConnected);
    if (status != napi_ok) {
        return false;
    }
    return isConnected;
#endif
    return true;
}


bool getifaddrs_ipv4_hotspot(std::string& _ifname, std::string& _ip) {
    return false;
}

//DEFINE_FIND_STATIC_METHOD(KPlatformCommC2Java_wakeupLock_new, KPlatformCommC2Java, "wakeupLock_new",
//                          "()Lcom/tencent/mars/comm/WakerLock;")
void* wakeupLock_new() {
    xverbose_function();

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(&wakeupLock_new);
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    
    napi_value result = JS_CallStaticMethodByName(env, "com.ohos.mars.comm.WakerLock", "constructor", 0);

    napi_ref ref;
	napi_status status = napi_create_reference(env, result, 1, &ref);
  	if (status != napi_ok) return NULL;
    return (void *)ref;
}

void  wakeupLock_delete(void* _object) {
    xverbose_function();
    xdebug2(TSF"_object= %0", _object);

    if (NULL == _object) return;

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&wakeupLock_delete, _object));
        
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    
	napi_delete_reference(env, (napi_ref)_object);
}

void  wakeupLock_Lock(void* _object) {
    xverbose_function();
    xassert2(_object);
    xdebug2(TSF"_object= %0", _object);

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&wakeupLock_Lock, _object));
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    
    napi_value real_obj;
    napi_status status = napi_get_reference_value(env, (napi_ref)_object, &real_obj);
                                                 
    JS_CallMethodByName(env, real_obj, "lock", 0);
}

void  wakeupLock_Lock_Timeout(void* _object, int64_t _timeout) {
    xverbose_function();
    xassert2(_object);
    xassert2(0 < _timeout);
    xverbose2(TSF"_object= %0, timeout= %1", _object, _timeout);

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&wakeupLock_Lock_Timeout, _object, _timeout));
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    
    napi_value timeoutValue;
    napi_status status = napi_create_int64(env, _timeout, &timeoutValue);
    if (status != napi_ok) {
        return;
    }
	napi_value real_obj;
    status = napi_get_reference_value(env, (napi_ref)_object, &real_obj);
    JS_CallMethodByName(env, real_obj, "lock", 1, timeoutValue);
}

void  wakeupLock_Unlock(void* _object) {
    xverbose_function();
    xassert2(_object);
    xdebug2(TSF"_object= %0", _object);

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&wakeupLock_Unlock, _object));      
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    
    napi_value real_obj;
    napi_status status = napi_get_reference_value(env, (napi_ref)_object, &real_obj);
                                                 
    JS_CallMethodByName(env, real_obj, "unlock", 0);
}

bool  wakeupLock_IsLocking(void* _object) {
    xverbose_function();
    xassert2(_object);

    if (coroutine::isCoroutine())
        return coroutine::MessageInvoke(boost::bind(&wakeupLock_IsLocking, _object));
    
    VarCache* cacheInstance = VarCache::Singleton();
    napi_env env = cacheInstance->GetJSEnv();
    
    napi_value real_obj;
    napi_status status = napi_get_reference_value(env, (napi_ref)_object, &real_obj);
                                                 
    napi_value result = JS_CallMethodByName(env, real_obj, "isLocking", 0);
    bool ret;
    status = napi_get_value_bool(env, result, &ret);
    if (status != napi_ok) {
        return false;
    }
    
    return ret;
}

}
}
